package cc.wsyw126.java.whatisjava.ThinkingInUnsafe;

import cc.wsyw126.java.model.User;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;


/**
 * Created by junpeng.yang on 16/12/28.
 */
public class ShadowCopy {

    public Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        return unsafe;
    }

    public Object shallowCopy(Object obj) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        long size = sizeOf(obj);
        long start = toAddress(obj);
        long address = getUnsafe().allocateMemory(size);
        getUnsafe().copyMemory(start, address, size);
        return fromAddress(address);
    }

    public long sizeOf(Object o) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        Unsafe u = getUnsafe();
        HashSet<Field> fields = new HashSet<>();
        Class c = o.getClass();
        while (c != Object.class) {
            for (Field f : c.getDeclaredFields()) {
                if ((f.getModifiers() & Modifier.STATIC) == 0) {
                    fields.add(f);
                }
            }
            c = c.getSuperclass();
        }
        // get offset
        long maxSize = 0;
        for (Field f : fields) {
            long offset = u.objectFieldOffset(f);
            if (offset > maxSize) {
                maxSize = offset;
            }
        }
        return ((maxSize / 8) + 1) * 8;   // padding
    }

    public long toAddress(Object obj) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        Object[] array = new Object[]{obj};
        long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
        return normalize(getUnsafe().getInt(array, baseOffset));
    }

    public Object fromAddress(long address) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        Object[] array = new Object[]{null};
        long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
        getUnsafe().putLong(array, baseOffset, address);
        return array[0];
    }

    private long normalize(int value) {
        if (value >= 0) return value;
        return (~0L >>> 32) & value;
    }

    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        ShadowCopy shadowCopy = new ShadowCopy();
        User user = new User();
        user.setName("zz");
        user.setId(123);
        User o = (User) shadowCopy.shallowCopy(user);
        System.out.println("o.getName() = " + o.getName());
    }


}
